import Foundation
import IOKit.serial
import IOKit //MARK: 这是电机用usb 连接 发送指令操作！！！


//MARK: 用的是Iokit串口方式

/// Class to manage serial port operations such as opening, closing, reading, and writing data.
class SerialPortManager {
    
    /// Flag to indicate whether messages are being shown in the logs.
    private var isShowingMessage: Bool = false
    
    /// File descriptor handle for the serial port.
    private var handle: Int32 = -1
    
    /// Flag to control the serial port reading loop.
    var stopReading = false
    
    /// Serial queue for input operations to ensure thread safety.
    let serialInQueue = DispatchSerialQueue(label: "serialInQueue")
    
    /// Serial queue for read operations to ensure thread safety.
    let serialReadQueue = DispatchSerialQueue(label: "serialReadQueue")
    
    /// Buffer to store instant received data from the serial port.
    var instantReceivedData: [UInt8] = []
    
    let isManualReceive: Bool = true
    
    var baudRate: Int = 9600
    
    func baudRateConstant(for baudRate: Int) -> speed_t {
        switch baudRate {
        case 2400: return speed_t(B2400)
        case 4800: return speed_t(B4800)
        case 9600: return speed_t(B9600)
        case 19200: return speed_t(B19200)
        case 38400: return speed_t(B38400)
        case 57600: return speed_t(B57600)
        case 115200: return speed_t(B115200)
        case 230400: return speed_t(B230400)
        // Add more if needed
        default: return speed_t(B38400)
        }
    }
    
    /// Initializes the SerialPortManager.
    /// - Parameter isShowingMessage: Boolean to indicate whether log messages should be displayed.
    init(baudRate: Int, isShowingMessage: Bool = false) {
        self.baudRate = baudRate
        self.isShowingMessage = isShowingMessage
    }
    
    /// Closes the serial port manually and clears the buffer.
    func closeManually() {
        closeSerialDevice()
        Thread.sleep(forTimeInterval: TimeInterval(0.05)) // Allow the system to stabilize after closing
        clearInstantReceivedData()
    }
    
    /// Retrieves the list of available serial devices on the system.
    /// - Returns: Array of strings representing the paths of serial devices.
    static func getSerialDeviceList() -> [String] {
        var deviceList: [String] = []
        
        let matchingDict = IOServiceMatching(kIOSerialBSDServiceValue)//MARK: 读取usb 数据
        var iterator: io_iterator_t = 0
        let result = IOServiceGetMatchingServices(kIOMainPortDefault, matchingDict, &iterator)
        
        if result == KERN_SUCCESS {
            var service: io_object_t
            while true {
                service = IOIteratorNext(iterator)
                if service == 0 { break }
                
                let devicePath = IORegistryEntryCreateCFProperty(service, kIOCalloutDeviceKey as CFString, kCFAllocatorDefault, 0)
                if let path = devicePath?.takeRetainedValue() as? String {
                    deviceList.append(path)
                }
                IOObjectRelease(service)
            }
            IOObjectRelease(iterator)
        }
        
        return deviceList
    }
    
    /// Opens the serial device with the specified name.
    /// - Parameter serialName: Name of the serial device to open.
    func openSerialDevice(serialName: String) {
        serialInQueue.async {
            self.handle = open(serialName, O_RDWR | O_NOCTTY | O_NONBLOCK)
            
            if self.handle != -1 {
                var attributes = termios()
                tcgetattr(self.handle, &attributes)
                let brc = self.baudRateConstant(for: self.baudRate)
                cfsetspeed(&attributes, brc) // Sets the baud rate to 9600
                attributes.c_cflag = tcflag_t(CS8 | CLOCAL | CREAD)
                attributes.c_iflag = tcflag_t(IGNPAR)
                
                attributes.c_oflag = 0
                attributes.c_lflag = 0
                tcsetattr(self.handle, TCSANOW, &attributes)
                 
                if (self.isManualReceive) {
                    self.receiveData(self.handle)
                }
                
                // Reading loop
                while !self.stopReading {
                    if (!self.isManualReceive) {
                        if let receivedData = self.receiveData(self.handle),
                           let receivedString = String(data: receivedData, encoding: .utf8) {
                            if self.isShowingMessage {
                            }
                        }
                    }
                    if self.stopReading { break }
                    Thread.sleep(forTimeInterval: TimeInterval(0.025))
                }
                
                self.closeSerialDevice() // Ensure device closure
            } else {
               }
        }
    }
    
    /// Checks if the serial port is currently open.
    /// - Returns: `true` if the port is open, otherwise `false`.
    func isSerialOpened() -> Bool {
        return (self.handle != -1)
    }
    
    /// Closes the serial port if it is open.
    func closeSerialDevice() {
        stopReading = true // Stop the reading loop
        if self.handle != -1 {
            print("CLOSING SERIAL: ", close(self.handle))//设备关闭
            self.handle = -1
               } else {
          
        }
    }
    
    /// Clears any leftover data in the read buffer.
    func clearReadBuffer() {
        var buffer = [UInt8](repeating: 0, count: 1024)
        let bytesRead = read(self.handle, &buffer, buffer.count)
        if bytesRead != -1 {
            print("LEFTOVER \(bytesRead) Bytes, \(Array(buffer[0..<bytesRead]))")
        }
    }
    
    /// Receives data from the serial port.
    /// - Parameter fileDescriptor: File descriptor of the serial port.
    /// - Returns: Data read from the serial port, or `nil` if no data is available.
    func receiveData(_ fileDescriptor: Int32) -> Data? {
        var buffer = [UInt8](repeating: 0, count: 1024)
        let bytesRead = read(fileDescriptor, &buffer, buffer.count)
        if bytesRead > 0 {
            if bytesRead <= 9 {
                if (isManualReceive) {
                        let thisReceivedData = Array(buffer[0..<bytesRead])
                        instantReceivedData.append(contentsOf: thisReceivedData)
                } else {
                    
                    serialReadQueue.asyncAndWait {
                        let thisReceivedData = Array(buffer[0..<bytesRead])
                        instantReceivedData.append(contentsOf: thisReceivedData)
                    }
                }
                return Data(bytes: buffer, count: bytesRead)
            } else {
                print("LEFTOVER \(bytesRead) Bytes, \(Array(buffer[0..<bytesRead]))")
            }
        }
        return nil
    }
    
    /// Retrieves the instant received data buffer.
    /// - Returns: Array of `UInt8` representing the received data.
    func getInstantReceivedData() -> [UInt8] {
        var data: [UInt8] = []
        serialReadQueue.sync {
            if (isManualReceive) {
               if let receivedData = self.receiveData(self.handle),
                   let receivedString = String(data: receivedData, encoding: .utf8) {
                   if self.isShowingMessage {
                   }
                }
            }
            data = self.instantReceivedData
        }
        return data
    }
    
    /// Clears the instant received data buffer.
    func clearInstantReceivedData() {
        serialReadQueue.sync {
            instantReceivedData = []
        }
    }
    
    /// Sends an array of `UInt8` data to the serial port.
    /// - Parameter data: Data to be sent.
    /// - Returns: Number of bytes written.
    func sendUInt8(data: [UInt8]) -> Int {
        DispatchQueue.global().async {
            if self.isShowingMessage {
                let hexString = data.map { String(format: "%02X", $0) }.joined(separator: " ")
            }
        }
        let data_ = Data(data)
        return sendData(data: data_)
    }
    
    /// Sends `Data` to the serial port.
    /// - Parameter data: `Data` to be sent.
    /// - Returns: Number of bytes written.
    ///
    func sendData(data: Data) -> Int {
        return write(self.handle, (data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count), data.count)
    }
}
